#include "net/Channel.h"
#include "net/EventLoop.h"
#include "net/Channel.h"
#include "Base/Logger.h"
#include "Base/CurrectThread.h"
#include <poll.h>
#include <errno.h>

namespace toym
{

namespace net
{

const int Channel::s_readEvent = POLLIN | POLLPRI;
const int Channel::s_writeEvent = POLLOUT;
const int Channel::s_nonEvent = 0;

Channel::Channel(int fd, EventLoop* loop)
    : m_fd(fd),
      m_idx(-1),    // 未加入poller的idx设置为-1，意为无效
      m_readCb(),
      m_writeCb(),
      m_closeCb(),
      m_errCb(),
      m_events(0),
      m_revents(0),
      m_loop(loop),
      m_tie(),
      m_tied(false)
{}

void Channel::handleEvents() {
    if (m_tied) {
        std::shared_ptr<void> obj = m_tie.lock();
        if(obj) {
            // 绑定的对象还存在
            handleEventsWithGurad();
        } else {
            LOG_INFO << "tied obj has destructed, fd " << m_fd;
        }
    } else {
        handleEventsWithGurad();
    }

}

void Channel::tie(const std::shared_ptr<void>& obj) {
    m_tie = obj;
    m_tied = true;
}

void Channel::update() {
    m_loop->updateChannel(*this);
}

void Channel::handleEventsWithGurad() {
    // POLLNVAL：无效的请求，文件描述符未打开
    if (m_revents & POLLNVAL) {
        LOG_WARNING << "Channel::handleEvents() POLLNVAL for fd " << m_fd ;
    }

    // POLLHUP: 当文件描述符是stream socket的时候，仅意味着对方关闭了
    //           当数据都被读取完后，对文件描述符再读会返回0（EOF）
    // 同时判断有POLLHUP位和无POLLIN位，说明读完了
    //  需要调用关闭
    if ((m_revents & POLLHUP) && !(m_revents & POLLIN)) {
        LOG_INFO << "Channel::handleEvents() POLLHUP  call close callback for fd " << m_fd;
        if (m_closeCb)
            m_closeCb();
        return ;
    }

    // POLLNVAL：文件描述符无效
    // POLLERR：出错的情况。在pipe里面，读端关闭时，写端poll这一位也会被设置
    if (m_revents & (POLLERR | POLLNVAL )) {
        CurrectThread::saveErrno(errno);
        LOG_ERROR << "Channel::handleEvent() POLLERR | POLLNVAL call error callback for fd " << m_fd;
        if (m_errCb)
            m_errCb();
        CurrectThread::saveErrno(0);
    }

    // POLLIN: 有数据要读
    // POLLPRI: TCP带外数据
    // POLLRDHUP: 流套接字，对方关闭了连接，或者关闭了写端
    if (m_revents & (POLLIN | POLLPRI | POLLRDHUP)) {
        LOG_INFO << "Channel::HandleEvent() POLLIN | POLLPRI | POLLRDHUP call read callback for fd " << m_fd;
        if(m_readCb)
            m_readCb();
    }

    // POLLOUT: 可写，没设置O_NONBLOCK的话写大规模数据会阻塞
    if (m_revents & POLLOUT) {
        LOG_INFO << "Channel::HandleEvent() POLLOUT call write callback for fd " << m_fd;
        if (m_writeCb)
            m_writeCb();
    }
}



} // namespace net


} // namespace toym
